package org.jetlinks.community.mstn.service;

import io.scalecube.services.exceptions.BadRequestException;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.crud.query.QueryHelper;
import org.hswebframework.web.system.authorization.api.entity.DimensionUserEntity;
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionUserService;
import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveUserService;
import org.jetlinks.community.auth.entity.OrganizationEntity;
import org.jetlinks.community.auth.entity.RoleEntity;
import org.jetlinks.community.auth.entity.UserDetailEntity;
import org.jetlinks.community.auth.enums.RoleState;
import org.jetlinks.community.auth.service.OrganizationService;
import org.jetlinks.community.auth.service.RoleService;
import org.jetlinks.community.auth.service.UserDetailService;
import org.jetlinks.community.auth.service.request.SaveUserRequest;
import org.jetlinks.community.device.service.AssetsService;
import org.jetlinks.community.mstn.entity.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@Service
@AllArgsConstructor
public class TenantService {

    private final ReactiveUserService userService;
    @Autowired
    private DefaultReactiveUserService reactiveUserService;
    private final RoleService roleService;
    private final OrganizationService organizationService;

    private final UserDetailService userDetailService;

    private final AssetsService assetsService;
    private final DefaultDimensionUserService dimensionUserService;
    //https://github.com/zhou-hao/hsweb4-examples/blob/main/hsweb4-webflux-example/src/main/java/org/hswebframework/example/crud/web/TestController.java#L36
    private final QueryHelper queryHelper;

    /**
     * 项目列表
     * @param queryParamEntity
     * @return
     */
    public Mono<PagerResult<TenantListResVO>> projectList(QueryParamEntity queryParamEntity) {
        return queryHelper.select(TenantListResVO.class)
            .all(UserDetailEntity.class, TenantListResVO::setContactInfo)
            .all(RoleEntity.class , TenantListResVO::setTenantInfo)
            .all(UserEntity.class, TenantListResVO::setUserInfo)
            .all(OrganizationEntity.class)
            .from(OrganizationEntity.class)
//            .leftJoin(DimensionUserEntity.class , join -> join.is(DimensionUserEntity::getUserId, OrganizationEntity::getContactUserId).and(DimensionUserEntity::getDimensionTypeId,"role"))
            .leftJoin(UserDetailEntity.class, join -> join.is(UserDetailEntity::getId, OrganizationEntity::getContactUserId))
            .leftJoin(RoleEntity.class, join -> join.is(RoleEntity::getId,OrganizationEntity::getPackageId))
            .leftJoin(UserEntity.class,join -> join.is(UserEntity::getId,UserDetailEntity::getId))
            .where(queryParamEntity)
            .fetchPaged();
    }

    /**
     * 保存用户,自动关联角色{@link SaveUserRequest#getRoleIdList()}以及机构(部门){@link SaveUserRequest#getOrgIdList()}
     * 先创建项目。在绑定用户和项目
     * @param createReqVO 保存请求
     * @return 用户ID
     */
    @Transactional
    public Mono<String> saveTenant(TenantCreateReqVO createReqVO) {
        OrganizationEntity organization = createReqVO.getOrganization();
        return organizationService
            .save(organization)
            .then(Mono.fromSupplier(organization::getId))
            .flatMap(orgId -> {
                SaveUserRequest request = createReqVO.getSaveUserRequest(orgId);
                return userDetailService.saveTenantUser(request,orgId,false);
            });
    }


    /**
     * 创建项目,绑定用户-机构关系,自动关联角色{@link SaveUserRequest#getRoleIdList()}以及机构(部门){@link SaveUserRequest#getOrgIdList()}
     *
     * @param createReqVO 保存请求
     * @return 用户ID
     */
    @Transactional
    public Mono<String> saveProject(ProjectCreateReqVO createReqVO) {
        OrganizationEntity organization = createReqVO.getMultiOrganization();
        return organizationService
            .save(organization)
            .then(Mono.fromSupplier(organization::getId))
            .flatMap(orgId -> Mono.empty()
                .then(userDetailService.bindUserTenantId(createReqVO.getUserId(), orgId))
                .then(roleService.bindUser(Collections.singleton(createReqVO.getUserId()), Collections.singleton(createReqVO.getRoleId()), false))
                .then(organizationService.bindTenantUser(Collections.singleton(createReqVO.getUserId()), orgId, false))
                .thenReturn(orgId));
    }

    /**
     * 修改项目
     * @param createReqVO
     * @return
     */
    @Transactional
    public Mono<Boolean> updateProject(ProjectCreateReqVO createReqVO){
        OrganizationEntity organization = createReqVO.getMultiOrganization();
        return organizationService
            .updateById(organization.getId(),organization).flatMap(
                result -> {
                    if (result == 0) {
                        return Mono.just(false);
                    }
                    return Mono.empty()
                        .then(roleService.bindUser(Collections.singleton(createReqVO.getUserId()), Collections.singleton(createReqVO.getRoleId()), true))
                        .thenReturn(result > 0);
                }
            );
    }

    /**
     * 查询项目详情
     */
    public Mono<ProjectDetailResVO> projectDetail(String id) {
        return organizationService.findById(id)
           .flatMap(organization -> {
               return roleService.findById(organization.getPackageId())
                       .map( roleEntity -> {
                           ProjectDetailResVO vo = ProjectDetailResVO.of(organization);
                           vo.setRoleInfo(roleEntity);
                           return vo;
                       });
            });
    }


    /**
     * 保存用户,自动关联角色{@link SaveUserRequest#getRoleIdList()}以及机构(部门){@link SaveUserRequest#getOrgIdList()}
     * 一个用户多个项目。 只创建用户。 在单独绑定项目
     * @param createReqVO 保存请求
     * @return 用户ID
     */
    @Transactional
    public Mono<String> saveMultiTenant(MultiTenantCreateReqVO createReqVO) {
        SaveUserRequest request = createReqVO.getSaveUserRequest();
        return userDetailService.saveUser(request);
    }

    @Transactional
    public Mono<Integer> updateTenantState(String id, RoleState state){
        OrganizationEntity entity = new OrganizationEntity();
        entity.setProjectState(state);
        return organizationService.updateById(id,entity)
                .then(Mono.just(id))
                //查询租户下所有用户
                .map(orgId -> dimensionUserService
                        .createQuery()
                        .where(DimensionUserEntity::getDimensionId,orgId)
                        .fetch()
                )
                .flatMap(dimensionUserEntity -> dimensionUserEntity.map(DimensionUserEntity::getUserId).collectList())
                //修改住户下所有用户状态
                .flatMap(userIdList -> {
                    UserEntity user = new UserEntity();
                    user.setStatus((byte) (state.equals(RoleState.enabled) ? 1:0));
                    return reactiveUserService.createUpdate()
                            .set(user::getStatus)
                            .where()
                            .in(UserEntity::getId,userIdList)
                            .execute();
                });
    }


    /**
     * 绑定用户到机构/项目/分组
     * @param userId
     * @param orgIdList
     * @param typeId
     * @return
     */
    @Transactional
    public Mono<Integer> bindUserToOrg(String userId, List<String> orgIdList, String typeId) {
        Flux<String> orgIdStream = Flux.fromIterable(orgIdList);

        return userDetailService
            .findUserDetail(userId)
            .flatMap(user -> orgIdStream
                .map(orgId -> {
                    DimensionUserEntity userEntity = new DimensionUserEntity();
                    userEntity.setUserId(userId);
                    userEntity.setUserName(user.getName());
                    userEntity.setDimensionId(orgId);
                    userEntity.setDimensionTypeId(typeId);
                    userEntity.setDimensionName(orgId);
                    return userEntity;
                })
                .as(dimensionUserService::save))
            .map(SaveResult::getTotal);
    }

    /**
     * 解绑用户到机构/项目/分组
     * @param userId
     * @param orgIdList
     * @param typeId
     * @return
     */
    @Transactional
    public Mono<Integer> unbindUserToOrg(String userId, List<String> orgIdList, String typeId) {
        Flux<String> orgIdStream = Flux.fromIterable(orgIdList);

        return orgIdStream
            .collectList()
            .filter(CollectionUtils::isNotEmpty)
            .flatMap(newOrgIdList -> dimensionUserService
                .createDelete()
                .where(DimensionUserEntity::getDimensionTypeId, typeId)
                .in(DimensionUserEntity::getDimensionId, newOrgIdList)
                .and(DimensionUserEntity::getUserId, userId)
                .execute())
            ;
    }


    /**
     * 创建站点/分组
     * @param type
     * @param organizationEntity
     * @return
     */
    @Transactional
    public Mono<String> createOrg(String type, OrganizationEntity organizationEntity) {
        return organizationService.findById(organizationEntity.getParentId())
            .flatMap(parent -> {
                if (parent == null) {
                    return Mono.error(new BadRequestException("父级机构不存在"));
                }
                //根据父节点，设置联系人
                organizationEntity.setContactUserId(parent.getContactUserId());
                //创建站点、分组
                return organizationService.insert(organizationEntity)
                    .then(Mono.fromSupplier(organizationEntity::getId))
                    .flatMap(orgId -> organizationService.findById(orgId)
                        .flatMap(typeEntity -> {
                            //绑定站点、分组给创建者和联系人
                            List<String > userIdList = new ArrayList<>();
                            userIdList.add(typeEntity.getContactUserId());
                            if (!Objects.equals(typeEntity.getCreatorId(), typeEntity.getContactUserId())) {
                                userIdList.add(typeEntity.getCreatorId());
                            }
                            return organizationService.bindUser(orgId, userIdList, type);
                        }).then(Mono.just(orgId)));
            });
    }

    /**
     * 删除分组
     */
    @Transactional
    public Mono<Boolean> removeGroup(String id) {
        return organizationService.findById(id)
           .flatMap(organizationEntity -> {
                if (organizationEntity == null) {
                    return Mono.error(new BadRequestException("机构不存在"));
                }
                //判断是否绑定资产
                return assetsService.isBind(id).collectList().flatMap(assets -> {
                    if (CollectionUtils.isNotEmpty(assets)) {
                        return Mono.error(new BadRequestException("该分组下有绑定资产，无法删除"));
                    }
                    else {
                        //删除分组
                        return organizationService.deleteById(id)
                            .then(Mono.just(true));
                    }
                });
            });
    }
}
